﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace JTActiveSafety.Gateway.Internal.test
{
    //public class KestrelConfigurationLoader
    //{
    //    private bool _loaded = false;

    //    internal KestrelConfigurationLoader(
    //        KestrelServerOptions options,
    //        IConfiguration configuration,
    //        IHostEnvironment hostEnvironment,
    //        bool reloadOnChange,
    //        ILogger<KestrelServer> logger,
    //        ILogger<HttpsConnectionMiddleware> httpsLogger)
    //    {
    //        Options = options ?? throw new ArgumentNullException(nameof(options));
    //        Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
    //        HostEnvironment = hostEnvironment ?? throw new ArgumentNullException(nameof(hostEnvironment));
    //        Logger = logger ?? throw new ArgumentNullException(nameof(logger));
    //        HttpsLogger = httpsLogger ?? throw new ArgumentNullException(nameof(logger));

    //        ReloadOnChange = reloadOnChange;

    //        ConfigurationReader = new ConfigurationReader(configuration);
    //        CertificateConfigLoader = new CertificateConfigLoader(hostEnvironment, logger);
    //    }

    //    public KestrelServerOptions Options { get; }
    //    public IConfiguration Configuration { get; internal set; }

    //    /// <summary>
    //    /// If <see langword="true" />, Kestrel will dynamically update endpoint bindings when configuration changes.
    //    /// This will only reload endpoints defined in the "Endpoints" section of your Kestrel configuration. Endpoints defined in code will not be reloaded.
    //    /// </summary>
    //    internal bool ReloadOnChange { get; }

    //    private IHostEnvironment HostEnvironment { get; }
    //    private ILogger<KestrelServer> Logger { get; }
    //    private ILogger<HttpsConnectionMiddleware> HttpsLogger { get; }

    //    private ConfigurationReader ConfigurationReader { get; set; }

    //    private ICertificateConfigLoader CertificateConfigLoader { get; }

    //    private IDictionary<string, Action<EndpointConfiguration>> EndpointConfigurations { get; }
    //        = new Dictionary<string, Action<EndpointConfiguration>>(0, StringComparer.OrdinalIgnoreCase);

    //    // Actions that will be delayed until Load so that they aren't applied if the configuration loader is replaced.
    //    private IList<Action> EndpointsToAdd { get; } = new List<Action>();

    //    private CertificateConfig DefaultCertificateConfig { get; set; }

    //    /// <summary>
    //    /// Specifies a configuration Action to run when an endpoint with the given name is loaded from configuration.
    //    /// </summary>
    //    public KestrelConfigurationLoader Endpoint(string name, Action<EndpointConfiguration> configureOptions)
    //    {
    //        if (string.IsNullOrEmpty(name))
    //        {
    //            throw new ArgumentNullException(nameof(name));
    //        }

    //        EndpointConfigurations[name] = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
    //        return this;
    //    }

    //    /// <summary>
    //    /// Bind to given IP address and port.
    //    /// </summary>
    //    public KestrelConfigurationLoader Endpoint(IPAddress address, int port) => Endpoint(address, port, _ => { });

    //    /// <summary>
    //    /// Bind to given IP address and port.
    //    /// </summary>
    //    public KestrelConfigurationLoader Endpoint(IPAddress address, int port, Action<ListenOptions> configure)
    //    {
    //        if (address == null)
    //        {
    //            throw new ArgumentNullException(nameof(address));
    //        }

    //        return Endpoint(new IPEndPoint(address, port), configure);
    //    }

    //    /// <summary>
    //    /// Bind to given IP endpoint.
    //    /// </summary>
    //    public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint) => Endpoint(endPoint, _ => { });

    //    /// <summary>
    //    /// Bind to given IP address and port.
    //    /// </summary>
    //    public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint, Action<ListenOptions> configure)
    //    {
    //        if (endPoint == null)
    //        {
    //            throw new ArgumentNullException(nameof(endPoint));
    //        }
    //        if (configure == null)
    //        {
    //            throw new ArgumentNullException(nameof(configure));
    //        }

    //        EndpointsToAdd.Add(() =>
    //        {
    //            Options.Listen(endPoint, configure);
    //        });

    //        return this;
    //    }

    //    /// <summary>
    //    /// Listens on ::1 and 127.0.0.1 with the given port. Requesting a dynamic port by specifying 0 is not supported
    //    /// for this type of endpoint.
    //    /// </summary>
    //    public KestrelConfigurationLoader LocalhostEndpoint(int port) => LocalhostEndpoint(port, options => { });

    //    /// <summary>
    //    /// Listens on ::1 and 127.0.0.1 with the given port. Requesting a dynamic port by specifying 0 is not supported
    //    /// for this type of endpoint.
    //    /// </summary>
    //    public KestrelConfigurationLoader LocalhostEndpoint(int port, Action<ListenOptions> configure)
    //    {
    //        if (configure == null)
    //        {
    //            throw new ArgumentNullException(nameof(configure));
    //        }

    //        EndpointsToAdd.Add(() =>
    //        {
    //            Options.ListenLocalhost(port, configure);
    //        });

    //        return this;
    //    }

    //    /// <summary>
    //    /// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
    //    /// </summary>
    //    public KestrelConfigurationLoader AnyIPEndpoint(int port) => AnyIPEndpoint(port, options => { });

    //    /// <summary>
    //    /// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
    //    /// </summary>
    //    public KestrelConfigurationLoader AnyIPEndpoint(int port, Action<ListenOptions> configure)
    //    {
    //        if (configure == null)
    //        {
    //            throw new ArgumentNullException(nameof(configure));
    //        }

    //        EndpointsToAdd.Add(() =>
    //        {
    //            Options.ListenAnyIP(port, configure);
    //        });

    //        return this;
    //    }

    //    /// <summary>
    //    /// Bind to given Unix domain socket path.
    //    /// </summary>
    //    public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath) => UnixSocketEndpoint(socketPath, _ => { });

    //    /// <summary>
    //    /// Bind to given Unix domain socket path.
    //    /// </summary>
    //    public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath, Action<ListenOptions> configure)
    //    {
    //        if (socketPath == null)
    //        {
    //            throw new ArgumentNullException(nameof(socketPath));
    //        }
    //        if (socketPath.Length == 0 || socketPath[0] != '/')
    //        {
    //            throw new ArgumentException(CoreStrings.UnixSocketPathMustBeAbsolute, nameof(socketPath));
    //        }
    //        if (configure == null)
    //        {
    //            throw new ArgumentNullException(nameof(configure));
    //        }

    //        EndpointsToAdd.Add(() =>
    //        {
    //            Options.ListenUnixSocket(socketPath, configure);
    //        });

    //        return this;
    //    }

    //    /// <summary>
    //    /// Open a socket file descriptor.
    //    /// </summary>
    //    public KestrelConfigurationLoader HandleEndpoint(ulong handle) => HandleEndpoint(handle, _ => { });

    //    /// <summary>
    //    /// Open a socket file descriptor.
    //    /// </summary>
    //    public KestrelConfigurationLoader HandleEndpoint(ulong handle, Action<ListenOptions> configure)
    //    {
    //        if (configure == null)
    //        {
    //            throw new ArgumentNullException(nameof(configure));
    //        }

    //        EndpointsToAdd.Add(() =>
    //        {
    //            Options.ListenHandle(handle, configure);
    //        });

    //        return this;
    //    }

    //    // Called from KestrelServerOptions.ApplyEndpointDefaults so it applies to even explicit Listen endpoints.
    //    // Does not require a call to Load.
    //    internal void ApplyEndpointDefaults(ListenOptions listenOptions)
    //    {
    //        var defaults = ConfigurationReader.EndpointDefaults;

    //        if (defaults.Protocols.HasValue)
    //        {
    //            listenOptions.Protocols = defaults.Protocols.Value;
    //        }
    //    }

    //    // Called from KestrelServerOptions.ApplyHttpsDefaults so it applies to even explicit Listen endpoints.
    //    // Does not require a call to Load.
    //    internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
    //    {
    //        var defaults = ConfigurationReader.EndpointDefaults;

    //        if (defaults.SslProtocols.HasValue)
    //        {
    //            httpsOptions.SslProtocols = defaults.SslProtocols.Value;
    //        }

    //        if (defaults.ClientCertificateMode.HasValue)
    //        {
    //            httpsOptions.ClientCertificateMode = defaults.ClientCertificateMode.Value;
    //        }
    //    }

    //    public void Load()
    //    {
    //        if (_loaded)
    //        {
    //            // The loader has already been run.
    //            return;
    //        }
    //        _loaded = true;

    //        Reload();

    //        foreach (var action in EndpointsToAdd)
    //        {
    //            action();
    //        }
    //    }

    //    // Adds endpoints from config to KestrelServerOptions.ConfigurationBackedListenOptions and configures some other options.
    //    // Any endpoints that were removed from the last time endpoints were loaded are returned.
    //    internal (List<ListenOptions>, List<ListenOptions>) Reload()
    //    {
    //        var endpointsToStop = Options.ConfigurationBackedListenOptions.ToList();
    //        var endpointsToStart = new List<ListenOptions>();

    //        Options.ConfigurationBackedListenOptions.Clear();
    //        DefaultCertificateConfig = null;

    //        ConfigurationReader = new ConfigurationReader(Configuration);

    //        LoadDefaultCert();

    //        foreach (var endpoint in ConfigurationReader.Endpoints)
    //        {
    //            var listenOptions = AddressBinder.ParseAddress(endpoint.Url, out var https);

    //            if (!https)
    //            {
    //                ConfigurationReader.ThrowIfContainsHttpsOnlyConfiguration(endpoint);
    //            }

    //            Options.ApplyEndpointDefaults(listenOptions);

    //            if (endpoint.Protocols.HasValue)
    //            {
    //                listenOptions.Protocols = endpoint.Protocols.Value;
    //            }
    //            else
    //            {
    //                // Ensure endpoint is reloaded if it used the default protocol and the protocol changed.
    //                // listenOptions.Protocols should already be set to this by ApplyEndpointDefaults.
    //                endpoint.Protocols = ConfigurationReader.EndpointDefaults.Protocols;
    //            }

    //            // Compare to UseHttps(httpsOptions => { })
    //            var httpsOptions = new HttpsConnectionAdapterOptions();

    //            if (https)
    //            {
    //                // Defaults
    //                Options.ApplyHttpsDefaults(httpsOptions);

    //                if (endpoint.SslProtocols.HasValue)
    //                {
    //                    httpsOptions.SslProtocols = endpoint.SslProtocols.Value;
    //                }
    //                else
    //                {
    //                    // Ensure endpoint is reloaded if it used the default protocol and the SslProtocols changed.
    //                    endpoint.SslProtocols = ConfigurationReader.EndpointDefaults.SslProtocols;
    //                }

    //                if (endpoint.ClientCertificateMode.HasValue)
    //                {
    //                    httpsOptions.ClientCertificateMode = endpoint.ClientCertificateMode.Value;
    //                }
    //                else
    //                {
    //                    // Ensure endpoint is reloaded if it used the default mode and the ClientCertificateMode changed.
    //                    endpoint.ClientCertificateMode = ConfigurationReader.EndpointDefaults.ClientCertificateMode;
    //                }

    //                // A cert specified directly on the endpoint overrides any defaults.
    //                httpsOptions.ServerCertificate = CertificateConfigLoader.LoadCertificate(endpoint.Certificate, endpoint.Name)
    //                    ?? httpsOptions.ServerCertificate;

    //                if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
    //                {
    //                    // Fallback
    //                    Options.ApplyDefaultCert(httpsOptions);

    //                    // Ensure endpoint is reloaded if it used the default certificate and the certificate changed.
    //                    endpoint.Certificate = DefaultCertificateConfig;
    //                }
    //            }

    //            // Now that defaults have been loaded, we can compare to the currently bound endpoints to see if the config changed.
    //            // There's no reason to rerun an EndpointConfigurations callback if nothing changed.
    //            var matchingBoundEndpoints = endpointsToStop.Where(o => o.EndpointConfig == endpoint).ToList();

    //            if (matchingBoundEndpoints.Count > 0)
    //            {
    //                endpointsToStop.RemoveAll(o => o.EndpointConfig == endpoint);
    //                Options.ConfigurationBackedListenOptions.AddRange(matchingBoundEndpoints);
    //                continue;
    //            }

    //            if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint))
    //            {
    //                var endpointConfig = new EndpointConfiguration(https, listenOptions, httpsOptions, endpoint.ConfigSection);
    //                configureEndpoint(endpointConfig);
    //            }

    //            // EndpointDefaults or configureEndpoint may have added an https adapter.
    //            if (https && !listenOptions.IsTls)
    //            {
    //                if (endpoint.Sni.Count == 0)
    //                {
    //                    if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
    //                    {
    //                        throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
    //                    }

    //                    listenOptions.UseHttps(httpsOptions);
    //                }
    //                else
    //                {
    //                    var sniOptionsSelector = new SniOptionsSelector(endpoint.Name, endpoint.Sni, CertificateConfigLoader, httpsOptions, listenOptions.Protocols, HttpsLogger);
    //                    listenOptions.UseHttps(SniOptionsSelector.OptionsCallback, sniOptionsSelector, httpsOptions.HandshakeTimeout);
    //                }
    //            }

    //            listenOptions.EndpointConfig = endpoint;

    //            endpointsToStart.Add(listenOptions);
    //            Options.ConfigurationBackedListenOptions.Add(listenOptions);
    //        }

    //        return (endpointsToStop, endpointsToStart);
    //    }

    //    private void LoadDefaultCert()
    //    {
    //        if (ConfigurationReader.Certificates.TryGetValue("Default", out var defaultCertConfig))
    //        {
    //            var defaultCert = CertificateConfigLoader.LoadCertificate(defaultCertConfig, "Default");
    //            if (defaultCert != null)
    //            {
    //                DefaultCertificateConfig = defaultCertConfig;
    //                Options.DefaultCertificate = defaultCert;
    //            }
    //        }
    //        else
    //        {
    //            var (certificate, certificateConfig) = FindDeveloperCertificateFile();
    //            if (certificate != null)
    //            {
    //                Logger.LocatedDevelopmentCertificate(certificate);
    //                DefaultCertificateConfig = certificateConfig;
    //                Options.DefaultCertificate = certificate;
    //            }
    //        }
    //    }

    //    private (X509Certificate2, CertificateConfig) FindDeveloperCertificateFile()
    //    {
    //        string certificatePath = null;
    //        try
    //        {
    //            if (ConfigurationReader.Certificates.TryGetValue("Development", out var certificateConfig) &&
    //                certificateConfig.Path == null &&
    //                certificateConfig.Password != null &&
    //                TryGetCertificatePath(out certificatePath) &&
    //                File.Exists(certificatePath))
    //            {
    //                var certificate = new X509Certificate2(certificatePath, certificateConfig.Password);

    //                if (IsDevelopmentCertificate(certificate))
    //                {
    //                    return (certificate, certificateConfig);
    //                }
    //            }
    //            else if (!string.IsNullOrEmpty(certificatePath))
    //            {
    //                Logger.FailedToLocateDevelopmentCertificateFile(certificatePath);
    //            }
    //        }
    //        catch (CryptographicException)
    //        {
    //            Logger.FailedToLoadDevelopmentCertificate(certificatePath);
    //        }

    //        return (null, null);
    //    }

    //    private static bool IsDevelopmentCertificate(X509Certificate2 certificate)
    //    {
    //        if (!string.Equals(certificate.Subject, "CN=localhost", StringComparison.Ordinal))
    //        {
    //            return false;
    //        }

    //        foreach (var ext in certificate.Extensions)
    //        {
    //            if (string.Equals(ext.Oid.Value, CertificateManager.AspNetHttpsOid, StringComparison.Ordinal))
    //            {
    //                return true;
    //            }
    //        }

    //        return false;
    //    }

    //    private bool TryGetCertificatePath(out string path)
    //    {
    //        // This will go away when we implement
    //        // https://github.com/aspnet/Hosting/issues/1294
    //        var appData = Environment.GetEnvironmentVariable("APPDATA");
    //        var home = Environment.GetEnvironmentVariable("HOME");
    //        var basePath = appData != null ? Path.Combine(appData, "ASP.NET", "https") : null;
    //        basePath = basePath ?? (home != null ? Path.Combine(home, ".aspnet", "https") : null);
    //        path = basePath != null ? Path.Combine(basePath, $"{HostEnvironment.ApplicationName}.pfx") : null;
    //        return path != null;
    //    }
    //}
}
